`ifndef ETH_PING_SVH
`define ETH_PING_SVH

class eth_ping;
    virtual mac_bfm bfm;
    /*
     * 静态属性
     */
    ping_pck_s   ping_pck;
    function new(virtual mac_bfm b);
        bfm = b;
        ping_config();
    endfunction

    function void ping_config ();
        byte stream[];
        //bit [31:0] old_crc = 32'hff_ff_ff_ff;
        bit [31:0] old_crc = 32'h00_00_00_00;

        this.ping_pck.head.preamble = 64'h55_55_55_55_55_55_55_d5;
        this.ping_pck.head.dst_mac_addr = DEV_MAC_ADDR;
        this.ping_pck.head.src_mac_addr = PC_MAC_ADDR;
        this.ping_pck.head.ether_type = IPV4;

        this.ping_pck.ip.version = 4'd4;
        this.ping_pck.ip.ihl = 4'd5;
        this.ping_pck.ip.tos = 8'h00;
        this.ping_pck.ip.tol_length = 16'd20+40;  // IP首部20字节 + IMCP(ping类型）40字节
        this.ping_pck.ip.id = 16'h4269;
        this.ping_pck.ip.flags = 3'b000;
        this.ping_pck.ip.frag_offset = 13'd0;
        this.ping_pck.ip.ttl = 8'h40;
        //this.ping_pck.ip.protocal = UDP;
        this.ping_pck.ip.protocal = 8'h01;
        this.ping_pck.ip.checksum = 16'h0000;
        this.ping_pck.ip.src_ip_addr = PC_IP_ADDR;
        this.ping_pck.ip.dst_ip_addr = DEV_IP_ADDR;

        this.ping_pck.icmp.type1 = 8'h08;
        this.ping_pck.icmp.code = 8'h00;
        this.ping_pck.icmp.checksum = 16'h4d3e;
        this.ping_pck.icmp.id = 16'h0001;
        this.ping_pck.icmp.seq_num = 16'h0024;
        this.ping_pck.icmp.data = '{16'h61, 16'h62, 16'h63, 16'h64, 16'h65, 16'h66, 16'h67, 16'h68, 16'h69, 16'h6a, 16'h6b, 16'h6c, 16'h6d, 16'h6e, 16'h6f, 16'h70, 16'h71, 16'h72, 16'h73, 16'h74, 16'h75, 16'h76, 16'h77, 16'h61, 16'h62, 16'h63, 16'h64, 16'h65, 16'h66, 16'h67, 16'h68, 16'h69};

        stream = {>>{this.ping_pck}};
        // 去掉前导符和最后的4个校验字节
        for(int i = 8; i < $size(stream)-4; i = i + 1 )
        begin
            // $display("crc first value %x", old_crc);
            //calc_crc(stream[i], old_crc);
            old_crc = calc_crc(stream[i], old_crc);
            // $display("crc input is %x; crc result is %x", stream[i], old_crc);
        end
        this.ping_pck.checksum = {old_crc[7:0], old_crc[15:8], old_crc[23:16], old_crc[31:24]};
    endfunction: ping_config

    function bit[32:0] calc_crc(bit [7:0] data, bit [31:0] init);
        reg [31:0] crc;
        reg        crc_feedback;
        integer    I;
        begin
            crc = ~ init;
            for (I = 0; I < 8; I = I + 1)
            begin
                crc_feedback = crc[0] ^ data[I];

                crc[0]       = crc[1];
                crc[1]       = crc[2];
                crc[2]       = crc[3];
                crc[3]       = crc[4];
                crc[4]       = crc[5];
                crc[5]       = crc[6]  ^ crc_feedback;
                crc[6]       = crc[7];
                crc[7]       = crc[8];
                crc[8]       = crc[9]  ^ crc_feedback;
                crc[9]       = crc[10] ^ crc_feedback;
                crc[10]      = crc[11];
                crc[11]      = crc[12];
                crc[12]      = crc[13];
                crc[13]      = crc[14];
                crc[14]      = crc[15];
                crc[15]      = crc[16] ^ crc_feedback;
                crc[16]      = crc[17];
                crc[17]      = crc[18];
                crc[18]      = crc[19];
                crc[19]      = crc[20] ^ crc_feedback;
                crc[20]      = crc[21] ^ crc_feedback;
                crc[21]      = crc[22] ^ crc_feedback;
                crc[22]      = crc[23];
                crc[23]      = crc[24] ^ crc_feedback;
                crc[24]      = crc[25] ^ crc_feedback;
                crc[25]      = crc[26];
                crc[26]      = crc[27] ^ crc_feedback;
                crc[27]      = crc[28] ^ crc_feedback;
                crc[28]      = crc[29];
                crc[29]      = crc[30] ^ crc_feedback;
                crc[30]      = crc[31] ^ crc_feedback;
                crc[31]      =           crc_feedback;
            end
            return ~ crc;
        end
    endfunction

    task send_ping();
        byte stream[];
        stream = {>>{this.ping_pck}};
        //$display("stream len is %d", $size(stream));
        bfm.gmii_rx_dv = 0;
        bfm.gmii_rx_er = 0;
        bfm.gmii_rxd = 0;
        //wait(bfm.phy_resetn)
        // @ (posedge bfm.phy_resetn);
        // #4us;
        #1us;
        for(int i = 0; i < $size(stream); i = i + 1 )
        begin
            @ (negedge bfm.gmii_rx_clk);
            bfm.gmii_rx_dv = 1;
            bfm.gmii_rxd = stream[i];
            // $display("stream %d is %x", i, stream[i]);
        end
        @ (negedge bfm.gmii_rx_clk);
        bfm.gmii_rx_dv = 0;
    endtask:send_ping
endclass

`endif
